package com.mjk.common.web.getway.transfer.dubbo;


import com.mjk.common.base.error.BeeError;
import com.mjk.common.base.response.BeeResponse;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.util.*;
import com.mjk.common.web.getway.constns.GetWayError;
import com.mjk.common.web.getway.transfer.GetwayTransfer;
import com.mjk.common.web.getway.transfer.dubbo.generic.ReferenceConfigCache;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 网关模块 需要配置DubboGetwayModel 对应的数据库 配置对应的映射关系 url -> dubbo接口
 * 调用方式：Object o = dubboGetWayProtocol.invoke(request.getRequestURI(),RequstParsUtils.getMapFromRequest(request));
* @author mjk  
* @date 2018年11月16日
 */
public class DubboGetWayProtocol implements GetwayTransfer,EnvironmentAware{
	
	private final Map<String,DubboGetwayModel> dubboMap = new ConcurrentHashMap<>();
	
	private final Map<String,GenericService> genericServiceMap = new ConcurrentHashMap<>();
	/**
	 * 执行转协议
	 */		
	@SuppressWarnings("unchecked")
	@Override
	public BeeResponse invoke(Map paramMap) {
		String version = MapUtils.getString(paramMap, "version");
		if(ObjectUtils.isEmpty(version)){
			return GetWayError.DUBBO_VERSION_IS_ERROR.tranceError("");
		}
		DubboGetwayModel dubboModel = dubboMap.get(version);
		GenericService genericService = genericServiceMap.get(version);
		if(null == dubboModel && (isMapping(version))){//如果是v1默认为manager
			dubboModel = dubboMap.get(DEFAULT_VERSION_MAPPING);
			genericService = genericServiceMap.get(DEFAULT_VERSION_MAPPING);
		}
		if(null == dubboModel){
			return GetWayError.DUBBO_VERSION_IS_ERROR.tranceError("");
		}		
		try {
			List<Map<String,Object>> paramMapList = buildInvokeParamInfos(dubboModel.getMethodName(),paramMap);
			int len = paramMapList.size();
			//参数类型 可以不填写
			String[] invokeParamTyeps = new String[len];
	        Object[] invokeParams = new Object[len];
	        for(int i = 0;i<len;i++) {
	        	for(Entry<String, Object> entry : paramMapList.get(i).entrySet()) {
	        		invokeParamTyeps[i] = entry.getKey();
	        		invokeParams[i] = entry.getValue();
	        	}
	        }	        
	        String methodName = dubboModel.getMethodName().substring(0, dubboModel.getMethodName().indexOf("("));
			return (BeeResponse) genericService.$invoke(methodName, invokeParamTyeps, invokeParams);
		}catch(Exception e){
			BeeResponse response = BeeError.BEE_ERROR_FOR_ADMIN.tranceError();
    	    response.setMsg("rpc service exception");
      	    return response;
		}
	}
	
	public List<Map<String,Object>> buildInvokeParamInfos(String methodInfo,Map<String,Object> paramMap){
		String paramsTypesStr = methodInfo.substring(methodInfo.indexOf("(")+1,methodInfo.indexOf(")"));
		//BizLogger.info(paramsTypesStr);
		//多参数
		List<Map<String,Object>> paramMapList = new ArrayList<>();
		if(paramsTypesStr.contains(",")) {
			//获取类型
			String[] paramsType = paramsTypesStr.split(",");
			for(String type : paramsType) {				
				paramMapList.add(paramInfo2Map(type,paramMap));				
			}
		}else {
			if(ObjectUtils.isNotEmpty(paramsTypesStr))
				paramMapList.add(paramInfo2Map(paramsTypesStr,paramMap));			
		}
		return paramMapList;
	}
	
	@SuppressWarnings({ "unchecked", "deprecation" })
	public Map<String,Object> paramInfo2Map(String paramInfo,Map<String,Object> paramMap){		
		Map<String,Object> map = new HashMap<>();
		if(paramInfo.contains(" ")) {			
			String methodType = paramInfo.split(" ")[0];//参数类型
			String paramsName = paramInfo.split(" ")[1];//参数名称
			Class<?> o = ClassUtil.loadClass(methodType);
			if(null == o) {				
				// 注意：如果参数类型是接口，或者List等丢失泛型，可通过class属性指定类型。
				BizLogger.info(paramMap.get(paramsName));
				Map<String, Object> person = GsonUtils.json2Map((String)paramMap.get(paramsName));
		        map.put(methodType, person);		        
			}else {
				if(ClassUtil.isBasicType(o.getClass())) {//基本数据类型
					map.put("java.lang.String", ObjectUtils.isEmpty(paramMap.get(paramsName))?GsonUtils.bean2Json(paramMap):
						paramMap.get(paramsName));
				}else if(o.isAssignableFrom(List.class)) {//list map 之类的类型
					map.put(methodType, GsonUtils.json2List((String)paramMap.get(paramsName)));
				}else if(o.isAssignableFrom(Map.class)) {
					map.put(methodType, GsonUtils.json2Map((String)paramMap.get(paramsName)));
				}else if(o.isAssignableFrom(Set.class)) {
					map.put(methodType, GsonUtils.json2Map((String)paramMap.get(paramsName)));
				}else if(o.isAssignableFrom(Date.class)) {
					map.put(methodType, new Date((String)paramMap.get(paramsName)));
				}else {
					Map<String, Object> person = GsonUtils.json2Map((String)paramMap.get(paramsName));
			        map.put(methodType, person);
				}
			}			
		}
		return map;
	}

		

	@SuppressWarnings("unchecked")
	@Override
	public void setEnvironment(Environment environment) {	
		String prefix = "bee.dubbo.";	
		String dubboName = environment.getProperty(prefix + "names");
		if (dubboName == null) {
			return;
		}
		for (String each : dubboName.split(",")) {			
			Map<String, Object> dubboProps = PropertyUtil.handle(environment, prefix + each, Map.class);
			if(CollectionUtils.isEmpty(dubboProps)){
				BizLogger.warn("Wrong datasource properties!");
				return;
			}			
			DubboGetwayModel model = new DubboGetwayModel();
			for(Entry<String,Object> entry : dubboProps.entrySet()){				
				ReflectionUtils.setFieldValue(model, entry.getKey(), entry.getValue());
			}
			dubboMap.put(each, model);
			genericServiceMap.put(each, genericServiceMap(model));
		}		
	}
	
	private GenericService genericServiceMap(DubboGetwayModel model){
		ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
		reference.setApplication(new ApplicationConfig(model.getApplication()));
		RegistryConfig registry = new RegistryConfig(model.getAddress());		
		registry.setProtocol("zookeeper");
		reference.setRegistry(registry);		
		if(!ObjectUtils.isEmpty(model.getGroup())) {
			reference.setGroup(model.getGroup());
		}
		if(!ObjectUtils.isEmpty(model.getVersion())) {
			reference.setVersion(model.getVersion());
		}
		if(ObjectUtils.isNotEmpty(model.getFilter())){
			reference.setFilter(model.getFilter());
		}
		reference.setCheck(false);
		reference.setInterface(model.getInterfaceName());		
		//泛化引用
		reference.setGeneric(true);
		reference.setFilter("dubboFilter");
		// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用  
		ReferenceConfigCache cache = ReferenceConfigCache.getCache();
        return cache.get(reference);
	}
}
